-
Notifications
You must be signed in to change notification settings - Fork 548
Replace error-prone instanceof in Rules classes #3858
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Replace error-prone instanceof in Rules classes #3858
Conversation
| && !$isFirstDeclaration | ||
| !$isFirstDeclaration | ||
| && !$method->isPrivate() | ||
| && ($returnType->isNull()->yes() || $returnType->isTrue()->yes() || $returnType->isFalse()->yes()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Logical operators are short-circuit evaluated, so rearranging expressions can lead to micro-optimizations.
ondrejmirtes
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't want to replace these calls by just accessing the first element. Union types should be properly handled. Multiple values should be iterated over.
bbbe15f to
776e845
Compare
|
Since |
9fc4e20 to
c57fe20
Compare
| $class = $type->getClassName(); | ||
| $referencedClassReflection = $type->getClassReflection(); | ||
| $classNames = $type->getObjectClassNames(); | ||
| sort($classNames); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
|
|
This pull request has been marked as ready for review. |
|
|
||
| $class = $type->getClassName(); | ||
| $referencedClassReflection = $type->getClassReflection(); | ||
| $classNames = $type->getObjectClassNames(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Introduce this var earlier so you don't need to call getObjectClassNames twice (methods called on Type can get expensive)
| foreach ($implementsTags as $implementsTag) { | ||
| $type = $implementsTag->getType(); | ||
| if (!$type instanceof ObjectType) { | ||
| if (count($type->getObjectClassNames()) === 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Introduce a var so you don't need to call getObjectClassNames twice
| { | ||
| if ($type instanceof ConstantStringType) { | ||
| return [$type]; | ||
| if (count($type->getConstantStrings()) > 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Introduce a var so you don't need to call getConstantStrings twice
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
| $arrayKeyValue = $keyType->toArrayKey(); | ||
| if ($arrayKeyValue instanceof ConstantIntegerType) { | ||
| $arrayKeyValues = $keyType->toArrayKey()->getConstantScalarValues(); | ||
| if (count($arrayKeyValues) === 1 && is_int($arrayKeyValues[0])) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it better/possible to handle union of scalar values here (?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean the following?
modified src/Rules/Arrays/DuplicateKeysInLiteralArraysRule.php
@@ -71,10 +71,12 @@ final class DuplicateKeysInLiteralArraysRule implements Rule
} else {
$keyType = $itemNode->getScope()->getType($key);
$arrayKeyValues = $keyType->toArrayKey()->getConstantScalarValues();
- if (count($arrayKeyValues) === 1 && is_int($arrayKeyValues[0])) {
- $autoGeneratedIndex = $autoGeneratedIndex === null
- ? $arrayKeyValues[0]
- : max($autoGeneratedIndex, $arrayKeyValues[0]);
+ foreach ($arrayKeyValues as $arrayKeyValue) {
+ if (is_int($arrayKeyValue)) {
+ $autoGeneratedIndex = $autoGeneratedIndex === null
+ ? $arrayKeyValue
+ : max($autoGeneratedIndex, $arrayKeyValue);
+ }
}
}Or is it type-level arithmetic?
modified src/Rules/Arrays/DuplicateKeysInLiteralArraysRule.php
@@ -42,7 +42,6 @@ final class DuplicateKeysInLiteralArraysRule implements Rule
$valueLines = [];
/**
- * @var int|false|null $autoGeneratedIndex
* - An int value represent the biggest integer used as array key.
* When no key is provided this value + 1 will be used.
* - Null is used as initializer instead of 0 to avoid issue with negative keys.
@@ -63,27 +62,37 @@ final class DuplicateKeysInLiteralArraysRule implements Rule
}
if ($autoGeneratedIndex === null) {
- $autoGeneratedIndex = 0;
- $keyType = new ConstantIntegerType(0);
+ $autoGeneratedIndex = new ConstantIntegerType(0);
} else {
- $keyType = new ConstantIntegerType(++$autoGeneratedIndex);
+ $autoGeneratedIndex = $scope->getType(new Plus(
+ new Int_(1),
+ new TypeExpr($autoGeneratedIndex),
+ ));
}
+ $keyType = $autoGeneratedIndex;
} else {
$keyType = $itemNode->getScope()->getType($key);
- $arrayKeyValues = $keyType->toArrayKey()->getConstantScalarValues();
- if (count($arrayKeyValues) === 1 && is_int($arrayKeyValues[0])) {
+ $arrayKeyType = $keyType->toArrayKey();
+ if ($autoGeneratedIndex !== false && $arrayKeyType->isInteger()->yes()) {
$autoGeneratedIndex = $autoGeneratedIndex === null
- ? $arrayKeyValues[0]
- : max($autoGeneratedIndex, $arrayKeyValues[0]);
+ ? $keyType
+ : $scope->getType(new FuncCall(
+ new Name('\max'),
+ [
+ new Arg(new TypeExpr($autoGeneratedIndex)),
+ new Arg(new TypeExpr($arrayKeyType)),
+ ]
+ ));
}
}$autoGeneratedIndex is explained as follows:
/**
* - An int value represent the biggest integer used as array key.
* When no key is provided this value + 1 will be used.
* - Null is used as initializer instead of 0 to avoid issue with negative keys.
* - False means a non-scalar value was encountered and we cannot be sure of the next keys.
*/
$autoGeneratedIndex = null;Since the key value actually changes depending on the input value, more appropriate typing is possible with type operations. However, since DuplicateKeysInLiteralArraysRule has some subtle behavior, I would like to keep the changes in this branch to a minimum.
| }; | ||
|
|
||
| if (!$nodeType->getValue()) { | ||
| if (in_array(false, $nodeType->getConstantScalarValues(), true)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure but isn't $nodeType->isFalse()->yes() better ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's correct, thanks!
| $assertValue = ($this->treatPhpDocTypesAsCertain ? $scope->getType($arg) : $scope->getNativeType($arg))->toBoolean(); | ||
| if (!$assertValue instanceof ConstantBooleanType) { | ||
| $assertValues = ($this->treatPhpDocTypesAsCertain ? $scope->getType($arg) : $scope->getNativeType($arg))->getConstantScalarValues(); | ||
| if (count($assertValues) !== 1 || !is_bool($assertValues[0])) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could maybe have kept the toBoolean and checked
$assertValue->isTrue()->yes() || $assertValue->isFalse()->yes()
b031d1d to
5d96286
Compare
|
This pull request has been marked as ready for review. |
| /** @var bool */ | ||
| return $assertValue->getConstantScalarValues()[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can return $assertValue->isTrue()->yes() if you want to avoid the var phpdoc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense!
5d96286 to
ddb9f66
Compare
|
I'd love to merge this, but please fix the conflicts. |
|
I was able to rebase this myself 🎉 |
ddb9f66 to
3e424f9
Compare
|
Thank you. |


Reduce usage of
$type instanceof *Typepattern in Rules classes.refs phpstan/phpstan#8311